// Copyright (c) 2022, the Dart project authors.  Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

import 'dart:io';

import 'package:_fe_analyzer_shared/src/util/relativize.dart';
import 'package:dart_style/dart_style.dart' show DartFormatter;

const String dataTypeOutputPrefix = 'datatype';

const List<int> dataTypeHierarchySizes = [
  1,
  2,
  3,
  4,
  5,
  6,
  7,
  8,
  12,
  16,
  24,
  32,
  48,
  64,
  96,
  128,
  192,
  256,
  // 512, too many nested expressions or statements
  // 1024,
];

void main() {
  Uri dataFolder = Platform.script.resolve('generated/');
  generateDataTypeTests(dataFolder);
  for (int size in dataTypeHierarchySizes) {
    generateDataTypeHierarchy(dataFolder, size);
  }
}

void generateDataTypeTests(Uri dataFolder) {
  Uri fileUri = dataFolder.resolve('${dataTypeOutputPrefix}.dart');
  StringBuffer sb = new StringBuffer();
  sb.writeln('''
// Copyright (c) 2024, the Dart project authors.  Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

// Generated by
// 
//   ${relativizeUri(Uri.base, Platform.script, isWindows)}

import '../test_datatypes.dart';''');

  for (int size in dataTypeHierarchySizes) {
    sb.writeln('''
import '${dataTypeOutputPrefix}$size.dart';''');
  }

  sb.writeln('''

List<Test> tests = [''');

  for (int size in dataTypeHierarchySizes) {
    sb.writeln('''
  Test<Base$size>(
      $size, 
      createData$size, 
      {dynamicDispatchStrategy: incByDynamicDispatch$size, 
       ifThenElseStrategy: incByIfThenElseDispatch$size,
       visitorStrategy: incByVisitorDispatch$size,
       patternStrategy: incByPatternDispatch$size}),
''');
  }

  sb.writeln('''
];
''');

  String result = new DartFormatter(
          languageVersion: DartFormatter.latestShortStyleLanguageVersion)
      .format(sb.toString());
  new File.fromUri(fileUri).writeAsStringSync(result);
}

void generateDataTypeHierarchy(Uri dataFolder, int size) {
  Uri fileUri = dataFolder.resolve('${dataTypeOutputPrefix}${size}.dart');
  StringBuffer sb = new StringBuffer();
  sb.writeln('''
// Copyright (c) 2024, the Dart project authors.  Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

// Generated by
// 
//   ${relativizeUri(Uri.base, Platform.script, isWindows)}
''');
  sb.writeln('''
import '../test_datatypes.dart';

abstract class Base$size {
  void dynamicDispatch(Counter counter);
  
  R accept<R, A>(Visitor$size<R, A> visitor, A arg);
}
''');
  for (int i = 0; i < size; i++) {
    sb.writeln('''
class Sub$i extends Base$size {
  @override
  void dynamicDispatch(Counter counter) {
    counter.inc();
  }
  
  void ifThenElseDispatch$i(Counter counter) {
    counter.inc();
  }
  
  void visitorDispatch$i(Counter counter) {
    counter.inc();
  }

  void patternDispatch$i(Counter counter) {
    counter.inc();
  }

  @override
  R accept<R, A>(Visitor$size<R, A> visitor, A arg) {
    return visitor.visitSub$i(this, arg);
  }
}
''');
  }
  sb.writeln('''
List<Base$size> createData$size() {
  return [''');
  for (int i = 0; i < size; i++) {
    sb.writeln('''
    Sub$i(),''');
  }
  sb.writeln('''
  ];
}
''');
  sb.writeln('''
void incByDynamicDispatch$size(Base$size base, Counter counter) {
  base.dynamicDispatch(counter);
}
''');

  sb.writeln('''
void incByIfThenElseDispatch$size(Base$size base, Counter counter) {''');
  for (int i = 0; i < size; i++) {
    if (i == 0) {
      sb.writeln('''
  if (base is Sub$i) {''');
    } else {
      sb.writeln('''
  } else if (base is Sub$i) {''');
    }
    sb.write('''
    base.ifThenElseDispatch$i(counter);
''');
  }
  sb.writeln('''
  }
}
''');

  sb.writeln('''
const Visitor$size<void, Counter> visitor = CounterVisitor$size(); 

void incByVisitorDispatch$size(Base$size base, Counter counter) {
  base.accept(visitor, counter);
}
''');

  sb.writeln('''
void incByPatternDispatch$size(Base$size base, Counter counter) {
  switch (base) {''');
  for (int i = 0; i < size; i++) {
    sb.writeln('''
    case Sub$i():
      base.patternDispatch$i(counter);
''');
  }
  sb.writeln('''
  }
}
''');

  sb.writeln('''
abstract class Visitor$size<R, A> {''');
  for (int i = 0; i < size; i++) {
    sb.writeln('''
  R visitSub$i(Sub$i sub, A arg);''');
  }
  sb.writeln('''
}
''');

  sb.writeln('''
class CounterVisitor$size implements Visitor$size<void, Counter> {
  const CounterVisitor$size();
''');

  for (int i = 0; i < size; i++) {
    sb.writeln('''
  @override
  void visitSub$i(Sub$i sub, Counter counter) {
    sub.visitorDispatch$i(counter);
  }
  ''');
  }
  sb.writeln('''
}
''');

  String result = new DartFormatter(
          languageVersion: DartFormatter.latestShortStyleLanguageVersion)
      .format(sb.toString());
  new File.fromUri(fileUri).writeAsStringSync(result);
}
